<script setup lang="ts">
import { type Ref, ref, watch, onMounted } from 'vue'

const props = defineProps({
  min: {
    type: Number,
    default: 0.0
  },
  max: {
    type: Number,
    default: 1.0
  },
  modelValue: {
    type: Number,
    default: 0.0
  },
  disabled: {
    type: Boolean,
    default: false
  }
})

const emits = defineEmits<{
  (e: 'update:modelValue', n: number): void
}>()

let p = 0
let p0 = 0
let innerValue = 0
let x0 = 0

const rail = ref<HTMLDivElement>() as Ref<HTMLDivElement>
const block = ref<HTMLDivElement>() as Ref<HTMLDivElement>

function updateBlockPosition(p: number) {
  block.value.style.left = `${Math.floor(p * 100)}%`
}

const onMouseMove = (e: MouseEvent) => {
  const w = rail.value.clientWidth
  const min = props.min
  const max = props.max
  p = 0
  innerValue = min
  if (w > 0 && max > min) {
    p = (e.pageX - x0) / w + p0
    innerValue = p * (max - min) + min
  }
  if (p < 0) {
    p = 0
    innerValue = min
  }
  if (p > 1.0) {
    p = 1.0
    innerValue = max
  }
  if (innerValue != props.modelValue) {
    emits('update:modelValue', innerValue)
  }
  updateBlockPosition(p)
}

const onMouseUp = () => {
  document.removeEventListener('mousemove', onMouseMove, true)
  document.removeEventListener('mouseup', onMouseUp, true)
}

function onBlockClicked(e: MouseEvent) {
  if (props.disabled) {
    return
  }
  x0 = e.pageX
  p0 = p
  document.addEventListener('mousemove', onMouseMove, true)
  document.addEventListener('mouseup', onMouseUp, true)
}

function updateInnerValue(v: number) {
  innerValue = v
  const min = props.min
  const max = props.max
  if (max <= min) {
    p = 0.0
  } else {
    p = (innerValue - min) / (max - min)
  }
  if (p < 0) {
    innerValue = min
    p = 0
  }
  if (p > 1) {
    innerValue = max
    p = 1.0
  }
}

watch(
  () => props.modelValue,
  (v) => {
    if (v == innerValue) {
      return
    }
    updateInnerValue(v)
    updateBlockPosition(p)
  }
)

onMounted(() => {
  updateInnerValue(props.modelValue)
  updateBlockPosition(p)
})
</script>
<template>
  <div class="slider" :class="{ 'is-disabled': disabled }">
    <div ref="rail" class="slider-rail">
      <div ref="block" class="slider-block" @click="onBlockClicked" />
    </div>
  </div>
</template>
